/**
 * Created by Weizehua on 2017/1/17.
 */
import {IncomingMessage, ServerResponse} from "http";
import * as fs from "fs";
import * as https from "https";
import {ServerOptions} from "https";
import {Dispatcher} from "./Core/Dispatcher/Dispatcher";
import {Singleton, Inject} from "ts.di";
import {Server} from "net";
import {logger} from "./Core/Logger/Logger";
import {serverPort} from "../Config/Config";
import * as querystring from"querystring";

export class HostInfo {
    ip: string;
}

@Singleton()
export class HttpsServer {
    errorNotice: string = "如果您在正常使用过程中出现此错误，请联系客服。";

    exec(options: ServerOptions, listeningPort: number, backlog: number) {
        return new Promise((resolve, reject) => {
            logger.debug('Spawning server ...');
            this.server = https.createServer(options)
                .on('request', (request, response) => this.onRequest(request, response))
                .on('error', (err: Error) => reject(err))
                .listen(listeningPort, null, backlog, () => {
                    this.onListen();
                    resolve();
                });
        })
    }

    onListen() {
        logger.debug(`server listenning on ${this.server.address().address}:${this.server.address().port}`);
    }

    async onRequest(request: IncomingMessage, response: ServerResponse) {
        try {
            // record url
            let url = request.url;
            logger.debug(`${Date()} : request url : ${url}`);

            // parse body
            let type:string = request.headers['content-type'];
            let bodyObj;
            if(type){
	        if(type.indexOf("json") !== -1)
	            bodyObj = await this.parseJsonBodyData(request);
	        else
                    bodyObj = await this.parseUrlQueryBodyData(request);
            }
            let responseBody = {};
            if (bodyObj) {
                // body is read, pass it to Dispatcher
                let hostInfo: HostInfo = {ip: request.connection.address().address};
                responseBody = await this.dispatcher.handle(url, hostInfo, bodyObj);
            }
            // finished, post back
            response.statusCode = 200;
            response.setHeader('Content-Type', 'application/json');
            response.setHeader('X-Powered-By', 'HappyXiaox');
            response.setHeader('access-control-allow-origin', '*');
            response.setHeader('access-control-allow-methods', 'GET, POST');
            response.setHeader('access-control-allow-headers', 'Content-Type');
            response.end(JSON.stringify(responseBody, null, 2));
        }
        catch (e) {
            response.statusCode = 500;
            response.setHeader('Content-Type', 'application/json');
            response.setHeader('X-Powered-By', 'HappyXiaox');
            response.setHeader('access-control-allow-origin', '*');
            response.setHeader('access-control-allow-methods', 'GET, POST');
            response.setHeader('access-control-allow-headers', 'Content-Type');
            response.end(JSON.stringify({success: false, reason: e.message + this.errorNotice}, null, 2));
            setTimeout(() => request.destroy(), 1000);
            logger.error(`error caught: ${e.name} : ${e.message}
stack trace: ${e.stack}`);
        }
    }

    private parseJsonBodyData(request): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            // try to read body as JSON
            let bodyData: any = [];
            request.on('data', chunk => {
                bodyData.push(chunk);
            }).on('end', () => {
                try {
                    let body = Buffer.concat(bodyData).toString();
                    if (body)
                        resolve(JSON.parse(body));
                    else
                        resolve("")
                }
                catch (e) {
                    reject(e)
                }
            });
        })
    }

    private parseUrlQueryBodyData(request): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            let bodyData: any = [];
            request.on('data', chunk => {
                bodyData.push(chunk);
            }).on('end', () => {
                try {
                    let body = Buffer.concat(bodyData).toString();
                    if (body){
                        let unEscaped = querystring.parse(body);
                        let parsed = JSON.parse(unEscaped.json);
                        resolve(parsed);
                    }
                    else
                        resolve("")
                }
                catch (e) {
                    reject(e)
                }
            });
        })
    }

    @Inject(() => Dispatcher)
    private dispatcher: Dispatcher;
    private server: Server;
}
